﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LiteFramework
{

    public abstract class AbsCache : ICache
    {
        public AbsCache()
        {
            this.ExpiryTime = DateTime.Now.AddMinutes(10); // 默认10分钟失效
        }

        /// <summary>
        /// 是否已过期。true已过期
        /// </summary>
        public bool IsExpired { get { return this.ExpiryTime < DateTime.Now; } }
        /// <summary>
        /// 过期时间
        /// </summary>
        public DateTime ExpiryTime { get; set; }
        /// <summary>
        /// 重置过期时间
        /// </summary>
        /// <param name="second">x秒后过期。小于0代表永不过期</param>
        public void ResetExpiryTime(int second)
        {
            if (second < 0)
                ExpiryTime = DateTime.MaxValue;
            else
                ExpiryTime = DateTime.Now.AddSeconds(second);
        }
    }

    public class CacheContainer<T> : ICacheContainer<T>
         where T : ICache
    {
        private readonly ConcurrentDictionary<string, T> caches = null;
        /// <summary>
        /// 上一次清理时间
        /// </summary>
        private DateTime lastClearAt = DateTime.Now;
        public CacheContainer()
        {
            caches = new ConcurrentDictionary<string, T>();
        }


        /// <summary>
        /// 清理间隔(秒)。默认300秒
        /// <para>　　小于0时，不清理</para>
        /// </summary>
        protected virtual int clearInterval { get { return 300; } }

        public event Func<string, T> retrieveFun;

        public void Add(string key, T value)
        {
            this.caches.AddOrUpdate(key, value, (x, y) => value.ExpiryTime > y.ExpiryTime ? value : y);
        }

        public T Get(string key)
        {
            #region 清除过期的缓存
            if (clearInterval > 0 && (DateTime.Now - lastClearAt).TotalSeconds > clearInterval && this.caches.Count > 0)
            {
                lastClearAt = DateTime.Now;
                lock (caches)
                {
                    string[] keys = new string[this.caches.Count];
                    this.caches.Keys.CopyTo(keys, 0);
                    T item;
                    foreach (var cacheKey in keys)
                    {
                        if (this.caches.TryGetValue(cacheKey, out item) && item.IsExpired)
                        {
                            this.caches.TryRemove(cacheKey, out item);
                        }
                    }
                }
            }
            #endregion

            T value;
            if (this.caches.TryGetValue(key, out value))
            {
                if (!value.IsExpired) return value;

                this.Remove(key);
            }

            if (this.retrieveFun != null)
            {
                value = this.retrieveFun(key);
                if (value != null)
                {
                    this.Add(key, value);
                    return value;
                }
            }

            return default(T);
        }

        public void Remove(string key)
        {
            T value;
            this.caches.TryRemove(key, out value);
        }

        public void Clear()
        {
            lock (this)
            {
                this.caches.Clear();
            }
        }
    }



    #region SysCache
    public class SysCache : AbsCache, IEnumerable
    {
        private static System.Web.HttpRuntime _httpRuntime = null;
        private TimeSpan _slidingExpiration = TimeSpan.Zero;

        public SysCache()
            : this(TimeSpan.FromSeconds(5 * 60))
        {
        }

        public SysCache(TimeSpan slidingExpiration)
        {
            this._slidingExpiration = slidingExpiration;
        }

        public void Add(string key, object value)
        {
            System.Web.Caching.CacheItemRemovedCallback onRemove = null;

            if (webCache[key] == null)
            {
                webCache.Add(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _slidingExpiration, System.Web.Caching.CacheItemPriority.Default, onRemove);
            }
            else
            {
                webCache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _slidingExpiration, System.Web.Caching.CacheItemPriority.Default, onRemove);
            }
        }

        public object Get(string key)
        {
            return webCache.Get(key);
        }

        public void Remove(string key)
        {
            webCache.Remove(key);
        }

        public void Clear()
        {
            System.Web.HttpRuntime.Close();
        }

        private System.Web.Caching.Cache webCache
        {
            get
            {
                if (_httpRuntime == null)
                {
                    lock (typeof(SysCache))
                    {
                        if (_httpRuntime == null)
                        {
                            _httpRuntime = new System.Web.HttpRuntime();
                        }
                    }
                }

                return System.Web.HttpRuntime.Cache;
            }
        }

        public IEnumerator GetEnumerator()
        {
            return webCache.GetEnumerator();
        }
    }
    #endregion
}